using iTextSharp.text.pdf;
using iTextSharp.text.pdf.parser;
using System;

namespace Pdf2epub.BusinessLogic.Extractors.ExtractStrategy
{
    public class TextChunk : IPdfElementChunk, IComparable<TextChunk>
    {
        internal float charSpaceWidth;
        internal float distParallelEnd;
        internal float distParallelStart;
        internal int distPerpendicular;
        internal Vector endLocation;
        internal DocumentFont Font;
        internal int FontSize;
        internal int orientationMagnitude;
        internal Vector orientationVector;
        internal Vector startLocation;
        internal String text;

        public TextChunk(
            string str,
            Vector startLocation,
            Vector endLocation,
            float charSpaceWidth,
            DocumentFont font,
            int curFontSize)
        {
            text = str;
            this.startLocation = startLocation;
            this.endLocation = endLocation;
            this.charSpaceWidth = charSpaceWidth;
            Font = font;
            FontSize = curFontSize;

            Vector oVector = endLocation.Subtract(startLocation);
            if (oVector.Length == 0)
            {
                oVector = new Vector(1, 0, 0);
            }
            orientationVector = oVector.Normalize();
            orientationMagnitude = (int)(Math.Atan2(orientationVector[Vector.I2], orientationVector[Vector.I1]) * 1000);

            // see http://mathworld.wolfram.com/Point-LineDistance2-Dimensional.html
            // the two vectors we are crossing are in the same plane, so the result will be purely
            // in the z-axis (out of plane) direction, so we just take the I3 component of the result
            Vector origin = new Vector(0, 0, 1);
            distPerpendicular = (int)(startLocation.Subtract(origin)).Cross(orientationVector)[Vector.I3];

            distParallelStart = orientationVector.Dot(startLocation);
            distParallelEnd = orientationVector.Dot(endLocation);
        }

        public float CharSpaceWidth
        {
            get { return charSpaceWidth; }
        }

        public String Text
        {
            get { return text; }
        }

        public float Width
        {
            get { return endLocation[Vector.I1] - startLocation[Vector.I1]; }
        }

        public float Height
        {
            get { return FontSize; }
        }

        public float XPos
        {
            get { return startLocation[Vector.I1]; }
        }

        public float YPos
        {
            get { return startLocation[Vector.I2]; }
        }

        public float MidLineHorizontal
        {
            get { return YPos + (Height/2); }
        }

        public float MidLineVertical
        {
            get { return XPos + (Width / 2); }
        }

        public int CompareTo(TextChunk rhs)
        {
            if (this == rhs) return 0; // not really needed, but just in case

            int rslt;
            rslt = CompareInts(orientationMagnitude, rhs.orientationMagnitude);
            if (rslt != 0) return rslt;

            rslt = CompareInts(distPerpendicular, rhs.distPerpendicular);
            if (rslt != 0) return rslt;

            // note: it's never safe to check floating point numbers for equality, and if two chunks
            // are truly right on top of each other, which one comes first or second just doesn't matter
            // so we arbitrarily choose this way.
            rslt = distParallelStart < rhs.distParallelStart ? -1 : 1;

            return rslt;
        }

        public float DistanceFromEndOf(TextChunk other)
        {
            float distance = distParallelStart - other.distParallelEnd;
            return distance;
        }

        public void PrintDiagnostics()
        {
            Console.Out.WriteLine("Text (@" + startLocation + " -> " + endLocation + "): " + text);
            Console.Out.WriteLine("orientationMagnitude: " + orientationMagnitude);
            Console.Out.WriteLine("distPerpendicular: " + distPerpendicular);
            Console.Out.WriteLine("distParallel: " + distParallelStart);
        }

        public bool SameLine(TextChunk a)
        {
            if (orientationMagnitude != a.orientationMagnitude) return false;
            if (distPerpendicular != a.distPerpendicular) return false;
            return true;
        }

        public bool SameParagraph(TextChunk lastChunk, int paragraphDistance)
        {
            if (lastChunk == null)
                return true;

            if (orientationMagnitude != lastChunk.orientationMagnitude)
                return false;
            if ((lastChunk.YPos - YPos) > paragraphDistance)
                return false;
            return true;
        }

        public override string ToString()
        {
            return string.Format("TextChunk: {0}/{1} '{2}'", XPos, YPos, Text);
        }

        private static int CompareInts(int int1, int int2)
        {
            return int1 == int2 ? 0 : int1 < int2 ? -1 : 1;
        }
    }
}